package com.chinatechstar.component.commons.utils;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

/**
 * 数字工具类
 *
 * @author Rei
 */
public class NumericUtils {
    /**
     * 判断BigDecimal是否是数字或者空
     *
     * @param num num
     * @return BigDecimal
     */
    public static BigDecimal numericOrZero(BigDecimal num) {
        if (num == null) {
            return new BigDecimal(0);
        } else {
            return num;
        }
    }

    /**
     * 判断BigDecimal是否为0或者空
     *
     * @param num num
     * @return BigDecimal
     */
    public static Boolean isNullOrZero(BigDecimal num) {
        if (num == null) {
            return true;
        }
        int res = numericOrZero(num).compareTo(new BigDecimal(0));
        return res == 0;
    }

    /**
     * 求平均数
     *
     * @param x     数组
     * @param scale 精度
     * @return BigDecimal
     */
    public static BigDecimal average(BigDecimal[] x, int scale) {
        if (x.length == 0) {
            return BigDecimal.ZERO;
        }
        int len = x.length;
        BigDecimal sum = BigDecimal.ZERO;

        // 求和
        for (BigDecimal num : x) {
            sum = sum.add(num);
        }

        // 求平均值
        return sum.divide(BigDecimal.valueOf(len), scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 方差s^2=[(x1-x)^2 +...(xn-x)^2]/n 或者s^2=[(x1-x)^2 +...(xn-x)^2]/(n-1)
     *
     * @param x     数组
     * @param scale 精度
     * @return BigDecimal
     */
    public static BigDecimal variance(BigDecimal[] x, int scale) {
        if (x.length == 0) {
            return BigDecimal.ZERO;
        }
        int len = x.length;

        // 求平均值
        BigDecimal dAve = average(x, scale);
        BigDecimal dVar = BigDecimal.ZERO;
        BigDecimal subtract;

        // 求方差
        for (BigDecimal num : x) {
            subtract = num.subtract(dAve);
            dVar = dVar.add(subtract.multiply(subtract));
        }
        return dVar.divide(BigDecimal.valueOf(len), scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 标准差σ=sqrt(s^2)
     * 结果精度：scale
     * 牛顿迭代法求大数开方
     *
     * @param x     数组
     * @param scale 精度
     * @return BigDecimal
     */
    public static BigDecimal standardDeviation(BigDecimal[] x, int scale) {
        // 方差
        BigDecimal variance = variance(x, scale);
        BigDecimal base2 = BigDecimal.valueOf(2.0);
        if (variance.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }

        // 计算精度
        int precision = 100;
        MathContext mc = new MathContext(precision, RoundingMode.HALF_UP);
        BigDecimal deviation = variance;
        int cnt = 0;
        while (cnt < precision) {
            deviation = (deviation.add(variance.divide(deviation, mc))).divide(base2, mc);
            cnt++;
        }
        deviation = deviation.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return deviation;
    }

}
